package shop.up.cart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import shop.up.cart.mapper.CartInfoMapper;
import shop.up.cart.service.CartAsyncService;
import shop.up.cart.service.CartService;
import shop.up.common.constant.RedisConst;
import shop.up.common.util.DateUtil;
import shop.up.model.cart.CartInfo;
import shop.up.model.product.SkuInfo;
import shop.up.product.client.ProductFeignClient;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author zzc
 * @date 2023/4/12 19:39
 */
@Service
public class CartServiceImpl implements CartService {

    @Resource
    private CartInfoMapper cartInfoMapper;

    @Resource
    private ProductFeignClient productFeignClient;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private CartAsyncService cartAsyncService;

    /**
     * 添加购物车
     *
     * @param skuId  商品id
     * @param userId 用户id
     * @param skuNum 商品数量
     */
    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        String cartKey = this.getCartKey(userId);
        //  判断缓存中是否有数据
        if (!redisTemplate.hasKey(cartKey)) {
            //  加载数据库并放入缓存
            loadCartCache(userId);
        }
        //  查询缓存 hget(key,field);
        CartInfo cartInfoExist = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());
        // 购物车中有该商品，将商品数量相加
        if (cartInfoExist != null) {
            // 商品数量相加
            cartInfoExist.setSkuNum(cartInfoExist.getSkuNum() + skuNum);
            // 更新实时价格
            cartInfoExist.setSkuPrice(productFeignClient.getSkuPrice(skuId));
            // 更新修改时间
            cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
            // 再次添加商品时，默认选中商品
            cartInfoExist.setIsChecked(1);
            // 更新数据库
            // cartInfoMapper.updateById(cartInfoExist);
            // 异步更新数据库
            cartAsyncService.updateCartInfo(cartInfoExist);
            // 更新缓存
            redisTemplate.boundHashOps(cartKey).put(skuId.toString(), cartInfoExist);
        } else { // 如果购物车中没有该商品
            // 第一次添加购物车
            CartInfo cartInfo = new CartInfo();
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            // 初始化时，购物车中商品价格
            cartInfo.setCartPrice(skuInfo.getPrice());
            // 数据库不存在时，购物车中商品价格
            cartInfo.setSkuPrice(skuInfo.getPrice());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
            cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
            // 插入数据库
            // cartInfoMapper.insert(cartInfo);
            // 异步插入数据库
            cartAsyncService.saveCartInfo(cartInfo);
            // 放入缓存
            redisTemplate.boundHashOps(cartKey).put(skuId.toString(), cartInfo);
            cartInfoExist = cartInfo;
        }
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(), cartInfoExist);
        setCartKeyExpire(cartKey);
    }

    /**
     * 通过用户Id查询购物车列表
     *
     * @param userId
     * @param userTempId
     * @return
     */
    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        // 返回的集合对象
        List<CartInfo> cartInfoList = new ArrayList<>();

        // 未登录：临时用户Id 获取未登录的购物车数据
        if (StringUtils.isEmpty(userId)) {
            cartInfoList = this.getCartList(userTempId);
            return cartInfoList;
        }

        //已登录
        if (!StringUtils.isEmpty(userId)) {
            List<CartInfo> cartInfoArrayList = this.getCartList(userTempId);
            if (!CollectionUtils.isEmpty(cartInfoArrayList)) {
                // 如果临时用户购物车有数据，则进行合并 合并的条件：skuId 相同
                cartInfoList = mergeToCartList(cartInfoArrayList, userId);
                // 删除未登录购物车数据
                deleteCartList(userTempId);
            }

            // 临时用户购物车中没有数据
            if (StringUtils.isEmpty(userTempId) || CollectionUtils.isEmpty(cartInfoArrayList)) {
                // 根据userId查询登录用户购物车
                cartInfoList = this.getCartList(userId);
            }
        }
        return cartInfoList;
    }

    /**
     * 更新选中状态
     *
     * @param userId
     * @param isChecked
     * @param skuId
     */
    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        // 异步修改数据库
        cartAsyncService.checkCart(userId, isChecked, skuId);
        // 修改缓存
        String cartKey = this.getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOperations =
                redisTemplate.boundHashOps(cartKey);
        // 先获取用户选中的商品
        if (hashOperations.hasKey(skuId.toString())) {
            CartInfo cartInfo = hashOperations.get(skuId.toString());
            cartInfo.setIsChecked(isChecked);
            // 更新缓存
            hashOperations.put(skuId.toString(), cartInfo);
            // 设置过期时间
            this.setCartKeyExpire(cartKey);
        }
    }

    /**
     * 删除登录用户购物车中指定skuId的商品
     *
     * @param skuId
     * @param userId
     */
    @Override
    public void deleteCart(Long skuId, String userId) {
        // 异步删除数据库中购物车中的商品
        cartAsyncService.deleteCartInfo(userId, skuId);
        // 删除缓存中购物车中的商品
        String cartKey = getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())) {
            hashOperations.delete(skuId.toString());
        }
    }

    /**
     * 根据用户id查询选中的购物车列表从缓存查
     * @param userId
     * @return
     */
    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();

        // 定义key user:userId:cart
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartCachInfoList = redisTemplate.opsForHash().values(cartKey);
        if (null != cartCachInfoList && cartCachInfoList.size() > 0) {
            for (CartInfo cartInfo : cartCachInfoList) {
                // 获取选中的商品！
                if (cartInfo.getIsChecked().intValue() == 1) {
                    cartInfoList.add(cartInfo);
                }
            }
        }
        return cartInfoList;
    }

    /**
     * 合并临时用户和登录用户的购物车
     *
     * @param cartInfoArrayList 临时用户购物车数据
     * @param userId            登录用户id
     * @return
     */
    private List<CartInfo> mergeToCartList(List<CartInfo> cartInfoArrayList, String userId) {
        // 根据用户 Id 查询登录的购物车数据
        List<CartInfo> cartInfoLoginList = this.getCartList(userId);
        // 登录购物车数据要是空，则直接返回
        if (CollectionUtils.isEmpty(cartInfoLoginList)) return cartInfoLoginList;
        // 登录用户的购物车数据不为空，根据skuId合并
        Map<Long, CartInfo> loginCartInfoMap = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
        // 遍历临时用户购物车列表
        for (CartInfo cartInfo : cartInfoArrayList) {
            Long skuId = cartInfo.getSkuId();
            // 登录用户的购物车中包含该商品,则商品数量相加，更新选中状态
            if (loginCartInfoMap.containsKey(skuId)) {
                CartInfo cartInfoLogin = loginCartInfoMap.get(skuId);
                // 合并商品数量
                cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum() + cartInfo.getSkuNum());
                // 更新update_time
                cartInfoLogin.setUpdateTime(new Timestamp(new Date().getTime()));
                // 合并购物车选中状态，以临时用户选中状态为基准
                if (cartInfo.getIsChecked() == 1) {
                    cartInfoLogin.setIsChecked(1);
                }
                //  更新数据库 同步更新
                QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("user_id", cartInfoLogin.getUserId());
                queryWrapper.eq("sku_id", cartInfoLogin.getSkuId());
                cartInfoMapper.update(cartInfoLogin, queryWrapper);
            } else { // 登录用户的购物车中不包含该商品，则保存该商品到登录用户的购物车中
                // 设置登录的用户id
                cartInfo.setUserId(userId);
                //  添加时间
                cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                cartInfoMapper.insert(cartInfo);
            }
        }
        //  从数据库中获取到最新合并的数据，然后放入缓存
        //  返回数据
        return this.loadCartCache(userId);
    }

    /**
     * 删除临时用户购物车数据
     *
     * @param userTempId
     */
    private void deleteCartList(String userTempId) {
        // 同步删除临时用户购物车数据
        // QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<CartInfo>();
        // queryWrapper.eq("user_id", userTempId);
        // cartInfoMapper.delete(queryWrapper);
        // 异步删除临时用户购物车数据
        cartAsyncService.deleteCartInfo(userTempId);
        String cartKey = getCartKey(userTempId);
        boolean flag = redisTemplate.hasKey(cartKey);
        if (flag) {
            redisTemplate.delete(cartKey);
        }
    }

    /**
     * 根据userId 查询购物车列表
     *
     * @param userId
     * @return
     */
    private List<CartInfo> getCartList(String userId) {
        // 声明一个返回的集合对象
        List<CartInfo> cartInfoList = new ArrayList<>();
        if (StringUtils.isEmpty(userId)) return cartInfoList;
        String cartKey = getCartKey(userId);
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        // 缓存中有数据
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            // 购物车列表有顺序，按照商品的更新时间降序
            cartInfoList.sort((o1, o2) ->
                    DateUtil.truncatedCompareTo(o2.getUpdateTime(), o1.getUpdateTime(), Calendar.SECOND)
            );
            return cartInfoList;
        } else {  // 缓存中没有数据,就查询数据库
            cartInfoList = loadCartCache(userId);
            return cartInfoList;
        }
    }

    /**
     * 通过 userId 查询购物车并放入缓存
     * @param userId
     */
    public List<CartInfo> loadCartCache(String userId) {
        QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId)
                .orderByDesc("update_time");
        List<CartInfo> cartInfoList = cartInfoMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList;
        }
        // 将数据放入缓存
        HashMap<String, CartInfo> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfoList) {
            BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfo.getSkuId());
            cartInfo.setSkuPrice(skuPrice);
            map.put(cartInfo.getSkuId().toString(), cartInfo);
        }
        String cartKey = getCartKey(userId);
        redisTemplate.opsForHash().putAll(cartKey, map);
        // 设置过期时间
        setCartKeyExpire(cartKey);
        return cartInfoList;
    }

    // 设置过期时间
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    // 获取购物车的key
    private String getCartKey(String userId) {
        //定义key user:userId:cart
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }
}
